﻿#ifndef _CELL_IOCP_SERVER_H_
#define _CELL_IOCP_SERVER_H_
#ifdef _WIN32

#include "CELLServer.hpp"
#include "CELLIOCP.hpp"
#include "CELLNetWork.hpp"
#include <iostream>

class CELLIOCPServer : public CELLServer
{
public:
	CELLIOCPServer()
	{
		_iocp.create();
	}

	~CELLIOCPServer()
	{
		Close();
	}

	virtual bool DoNetEvents() override
	{
		CELLClient* pClient = nullptr;
		// 检测需要发送给客户端数据的客户端(这个可以根据情况而改变)
		for (auto iter = _clients.begin(); iter != _clients.end(); )
		{
			pClient = iter->second;
			if (pClient->needWrite())
			{
				auto pIoData = pClient->makeSendIOData();
				if (pIoData)
				{
					if (!_iocp.postSend(pIoData))
					{
						OnClientLeave(pClient);
						iter = _clients.erase(iter);
						continue;
					}
				}
				//--
				pIoData = pClient->makeRecvIOData();
				if (pIoData)
				{
					if (!_iocp.postRecv(pIoData))
					{
						OnClientLeave(pClient);
						iter = _clients.erase(iter);
						continue;
					}
				}
			}
			else {
				auto pIoData = pClient->makeRecvIOData();
				if (pIoData)
				{
					if (!_iocp.postRecv(pIoData))
					{
						OnClientLeave(pClient);
						iter = _clients.erase(iter);
						continue;
					}
				}
			}
			iter++;
		}

		// 循环处理事件直到没有事件发生为止
		while (true)
		{
			int ret = DoNetIOCPEvents();
			if (ret < 0)
			{
				// IOCP出错
				return false;
			}
			else if (ret == 0)
			{
				// 没有事件发生
				return true;
			}
		}

		return true;
	}

	// 每次只处理一件网络事件
	/*
		返回值 ret：
		   -1: iocp出错
			0: 没有事件发生
			1: 有事件发生
	*/
	int DoNetIOCPEvents()
	{
		time_t time = _time_events.getTime();
		if (time <= 0)
		{
			time = INFINITE;
		}
		int ret = _iocp.wait(_ioEvent, time);
		do {
			if (ret < 0)
			{
				// IOCP出错
				CELLLog_Error("CELLIOCPServer::DoNetEvents");
				break;
			}
			else if (ret == 0)
			{
				// 没有事件发生
				break;
			}

			// 接收数据
			if (IO_TYPE::RECV == _ioEvent.pIOData->iotype)
			{// 接收数据完成
				if (_ioEvent.bytesTrans <= 0)
				{// 客户端断开
					CELLLog_Info("RECV close sockfd=%d, bytesTrans=%d", (int)_ioEvent.pIOData->sockfd, _ioEvent.bytesTrans);
					rmClient(_ioEvent);
					break;
				}
				// 告诉接收数据的客户端本次接收到了多少数据，用于接收缓冲区的数据位置的偏移
				CELLClient* pClient = (CELLClient*)_ioEvent.data.ptr;
				if (pClient)
					pClient->recv4Iocp(_ioEvent.bytesTrans);
				// 接收消息计数
				OnNetRecv(pClient);
				// CELLLog_Info("recv data: sockfd=%d, bytesTrans=%d", (int)_ioEvent.pIOData->sockfd, _ioEvent.bytesTrans);
			}
			else if (IO_TYPE::SEND == _ioEvent.pIOData->iotype)
			{// 发送数据完成
				if (_ioEvent.bytesTrans <= 0)
				{// 客户端断开
					CELLLog_Info("SEND close sockfd=%d, bytesTrans=%d", (int)_ioEvent.pIOData->sockfd, _ioEvent.bytesTrans);
					rmClient(_ioEvent);
					break;
				}
				// 通知发送缓冲区发送了多少数据
				CELLClient* pClient = (CELLClient*)_ioEvent.data.ptr;
				if (pClient)
					pClient->send2Iocp(_ioEvent.bytesTrans);
				// CELLLog_Info("send data: sockfd=%d, bytesTrans=%d", (int)_ioEvent.pIOData->sockfd, _ioEvent.bytesTrans);
			}
			else
			{
				//
				CELLLog_Error("undefined iotype");
			}
		} while (0);
		//处理定时器事件
		_time_events.process_timer();
		return ret;
	}

	// 本类中加入客户端加入事件
	virtual void OnClientJoin(CELLClient* pClient)
	{
		_iocp.reg(pClient->sockfd(), (void*)pClient);
		// 获取本次要接收的数据存放在哪里
		auto pIoData = pClient->makeRecvIOData();
		// 如果获取到了(接收缓冲区有空间)就向IOCP投递接收数据的任务
		if (pIoData)
			_iocp.postRecv(pIoData);
	}

	// 
	void rmClient(IO_EVENT ioEvent)
	{
		// 在心跳检测如果检测到了并关闭了socket，会使IOCP收到客户端断开的事件，
		// 而且在心跳检测哪里只关闭了socket，并没有释放client，这里就能直接获取到
		// 在rmClient函数中调用了客户端离开事件，在这里会释放client空间
		CELLClient* pClient = (CELLClient*)_ioEvent.data.ptr;
		if (pClient)
			rmClient(pClient);
	}

	// 真正地删除客户端并触发离开事件
	void rmClient(CELLClient* pClient)
	{
		auto iter = _clients.find(pClient->sockfd());
		if (iter != _clients.end())
			_clients.erase(iter);
		//
		OnClientLeave(pClient);
	}
private:
	// IOCP实例
	CELLIOCP _iocp;
	// 用于_iocp.wait中第一个参数
	IO_EVENT _ioEvent = { 0 };
};

#endif // _WIN32

#endif // #ifndef _CELL_IOCP_SERVER_H_
